package com.xuyuan.entity;

import com.xuyuan.util.ShaUtilUtil;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author xuyuan
 * @desc 区块：区块链中的每一个节点，存储着上一个块的哈希值，数据，时间戳，随机数，以及hash值。
 */
public class Block {

    public String hash;
    public String previousHash;
    public String merkleRoot;
    public List<Transaction> transactions = new ArrayList<>(); // our data will be a simple message.
    private long timeStamp;
    private int nonce; // 我们将要求矿工通过在块中尝试不同的变量值来进行工作量证明，直到其哈希值以一定数量的 0 开头。

    public Block(String previousHash) {
        this.previousHash = previousHash;
        this.timeStamp = new Date().getTime();
        this.hash = calculateHash();
    }

    public Block(String data, String previousHash) {
        this.previousHash = previousHash;
        this.timeStamp = new Date().getTime();
        this.merkleRoot = data;
        this.hash = calculateHash();
    }

    public String calculateHash() {
        String calculatedhash = ShaUtilUtil.applySha256(previousHash + Long.toString(timeStamp) + Integer.toString(nonce) + merkleRoot);
        return calculatedhash;
    }

    public void mineBlock(int difficulty) {
        String target = new String(new char[difficulty]).replace('\0', '0'); // Create a string with difficulty * "0"
        while (!hash.substring(0, difficulty).equals(target)) {
            nonce++;
            hash = calculateHash();
        }
        System.out.println("Block Mined!!! : " + hash);
    }

    public boolean addTransaction(Transaction transaction) {
        // process transaction and check if valid, unless block is genesis block then ignore.
        if (transaction == null) return false;
        if ((previousHash != "0")) {
            if ((transaction.processTransaction() != true)) {
                System.out.println("Transaction failed to process. Discarded.");
                return false;
            }
        }
        transactions.add(transaction);
        System.out.println("Transaction Successfully added to Block");
        return true;
    }

}